// Pong VGA game
// (c) fpga4fun.com

module VGAController(clk, invert, reset, plyr_input, plyr2_input, vga_h_sync, vga_v_sync, R, G, B, CounterX, CounterY, tile, draw_enable, objXStart, objYStart, refreshRate);

//If draw is enabled the objX and objY is the location to draw (top left pixel (top left corner --> always) cannot be any other pixel for that tile)
input [9:0] objXStart;
input [8:0] objYStart;
input [11:0] plyr_input, plyr2_input;
input draw_enable;
input reset;
input refreshRate;
output [9:0] CounterX;
output [8:0] CounterY;

input invert;
input clk;
wire clk_25mhz;
output vga_h_sync, vga_v_sync;
output [2:0] R, G;
output [1:0] B;

reg [2:0] vga_R, vga_G, vga_Rbuf1, vga_Gbuf1, vga_Rbuf2, vga_Gbuf2;
reg [1:0] vga_B, vga_Bbuf1, vga_Bbuf2;



parameter bg_color = 3'b000;
//ram mux
//mux31_16bit RAM_DO_mux(DO_1,DO_2,DO_3,DO,ADDR[12:11]);

reg count = 0;

//output [11:0] plyr_input;
//output latch,pulse;
//input data;
//
////SNESController SNES(clk, 1'b0, latch, pulse, data, plyr_input);
//
//parameter bx = 150;
//parameter by = 150;
//parameter p1 = 200;
//
//wire [9:0] ball_X;
//wire [8:0] ball_Y; 
//wire [8:0] P1_paddle;
//
//assign ball_X = bx;//plyr_input[9:0];
//assign ball_Y = by;//plyr_input[11:3];
//assign P1_paddle = p1;
reg invertColor;
wire inDisplayArea;
wire [9:0] CounterX;
wire [8:0] CounterY;

input [3:0] tile;
wire [3:0] current_tile;
assign current_tile = tile;
//
//collisionDetection gameObjectsDrawer(clk, CounterX, CounterY, objX, objY, ball_X, ball_Y, P1_paddle, P2_paddle, tile, draw_enable);

reg [7:0] DO;
wire [7:0] DI, DO_BG, DO_1, DO_2, DO_3, DO_4, DO_5, DO_6, DO_7, DO_8;
reg [9:0] ADDR = 0;

//DO is the drawer data... Set DO to DO_1..2..3.. depending on current tile to draw.
VGABlock1 tennisBall (
  .clka(clk), // input clka
  .wea(1'b0), // input [0 : 0] wea
  .addra(ADDR), // input [9 : 0] addra
  .dina(DI), // input [7 : 0] dina
  .douta(DO_1) // output [7 : 0] douta
);

//DO is the drawer data... Set DO to DO_1..2..3.. depending on current tile to draw.
VGABlock2 BLK_WHITE_Paddle ( //black = left 16 pixels, white = right 16 pixels
  .clka(clk), // input clka
  .wea(1'b0), // input [0 : 0] wea
  .addra(ADDR), // input [9 : 0] addra
  .dina(DI), // input [7 : 0] dina
  .douta(DO_2) // output [7 : 0] douta
);

//DO is the drawer data... Set DO to DO_1..2..3.. depending on current tile to draw.
VGABlock3 WhiteCueBALL ( 
  .clka(clk), // input clka
  .wea(1'b0), // input [0 : 0] wea
  .addra(ADDR), // input [9 : 0] addra
  .dina(DI), // input [7 : 0] dina
  .douta(DO_3) // output [7 : 0] douta
);

//DO is the drawer data... Set DO to DO_1..2..3.. depending on current tile to draw.
VGABlock4 ElasticBall_Inverted ( //Inverted means the background is white by default
  .clka(clk), // input clka
  .wea(1'b0), // input [0 : 0] wea
  .addra(ADDR), // input [9 : 0] addra
  .dina(DI), // input [7 : 0] dina
  .douta(DO_4) // output [7 : 0] douta
);

//DO is the drawer data... Set DO to DO_1..2..3.. depending on current tile to draw.
VGABlock5 ScribbleBall_Inverted ( //Inverted means the background is white by default
  .clka(clk), // input clka
  .wea(1'b0), // input [0 : 0] wea
  .addra(ADDR), // input [9 : 0] addra
  .dina(DI), // input [7 : 0] dina
  .douta(DO_5) // output [7 : 0] douta
);

//DO is the drawer data... Set DO to DO_1..2..3.. depending on current tile to draw.
VGABlock6 Black3dLettersBall (
  .clka(clk), // input clka
  .wea(1'b0), // input [0 : 0] wea
  .addra(ADDR), // input [9 : 0] addra
  .dina(DI), // input [7 : 0] dina
  .douta(DO_6) // output [7 : 0] douta
);

//DO is the drawer data... Set DO to DO_1..2..3.. depending on current tile to draw.
VGABlock7 Black3dLettersPaddle (
  .clka(clk), // input clka
  .wea(1'b0), // input [0 : 0] wea
  .addra(ADDR), // input [9 : 0] addra
  .dina(DI), // input [7 : 0] dina
  .douta(DO_7) // output [7 : 0] douta
);

//DO is the drawer data... Set DO to DO_1..2..3.. depending on current tile to draw.
VGABlock8 SpecPaddle (
  .clka(clk), // input clka
  .wea(1'b0), // input [0 : 0] wea
  .addra(ADDR), // input [9 : 0] addra
  .dina(DI), // input [7 : 0] dina
  .douta(DO_8) // output [7 : 0] douta
);

assign DO_BG = 0;
//VGARAM VGA_Block1(DI,DO_BG,ADDR[9:0],clk,reset,1'b0,1'b1);

//VGARAM VGA_Block2(DI,DO_2,ADDR[10:1],clk,reset,1'b0,1'b1);
//VGARAM VGA_Block3(DI,DO_3,ADDR[10:1],clk,reset,1'b0,1'b1);

//VGARAM Tile_RAM(TileDI, TileDO,TileADDR,clk,reset,1'b0,1'b1);
 


hvsync_generator syncgen(.clk(clk_25mhz), .vga_h_sync(vga_h_sync), .vga_v_sync(vga_v_sync), 
  .inDisplayArea(inDisplayArea), .CounterX(CounterX), .CounterY(CounterY));

//assgin ADDR[10:1] = CounterX - 320;

 
reg clockcount = 1;
reg [9:0] offsetY =10'b0, offsetX = 10'b0;


always@(posedge clk) begin
	if (clockcount == 1)
		clockcount <= 0;
	else 
		clockcount <= clockcount + 1;
end

assign clk_25mhz = (clockcount == 1);


/*
always @(posedge clk_25mhz)
begin 
	if(invert) begin

	R[2] <= ~vga_Rbuf2[2] & inDisplayArea;
	G[2] <= ~vga_Gbuf2[2] & inDisplayArea;
	B[1] <= ~vga_Bbuf2[1] & inDisplayArea;
	
	R[1] <= ~vga_Rbuf2[1] & inDisplayArea;
	G[1] <= ~vga_Gbuf2[1] & inDisplayArea;
	B[0] <= ~vga_Bbuf2[0] & inDisplayArea;
	
	R[0] <= ~vga_Rbuf2[0] & inDisplayArea;
	G[0] <= ~vga_Gbuf2[0] & inDisplayArea;
	
	end
	else begin 
	
	R[2] <= vga_R[2] & inDisplayArea;
	G[2] <= vga_G[2] & inDisplayArea;
	B[1] <= vga_B[1] & inDisplayArea;
	
	R[1] <= vga_R[1] & inDisplayArea;
	G[1] <= vga_G[1] & inDisplayArea;
	B[0] <= vga_B[0] & inDisplayArea;
	
	R[0] <= vga_R[0] & inDisplayArea;
	G[0] <= vga_G[0] & inDisplayArea;
	
	end

end
*/

assign R[2] = vga_Rbuf2[2] & inDisplayArea;
assign G[2] = vga_Gbuf2[2] & inDisplayArea;
assign B[1] = vga_Bbuf2[1] & inDisplayArea;
	
assign R[1] = vga_Rbuf2[1] & inDisplayArea;
assign G[1] = vga_Gbuf2[1] & inDisplayArea;
assign B[0] = vga_Bbuf2[0] & inDisplayArea;
	
assign R[0] = vga_Rbuf2[0] & inDisplayArea;
assign G[0] = vga_Gbuf2[0] & inDisplayArea;

//tile selector

parameter P1paddle = 1;
parameter P2paddle  = 2;
parameter ball = 0;

//reg [2:0] curBallTile = 0;
////test to trigger a new ball tile every time the switch is hit
//always @(posedge SW2) begin
//
//if(curBallTile == 5)
//	curBallTile = 0;
//else
//	curBallTile = curBallTile+1;
//end

parameter MaxBallTiles = 5; //6-1
parameter MaxPaddles = 2;
parameter MaxPaddlesSnds = 1;

reg [3:0] curBallTile = 0;
reg [3:0] curPaddleTileP1 = 0;
reg [3:0] curPaddleTileP2 = 0;
reg [3:0] curPaddleSound = 0;

always @(posedge refreshRate) begin
//clk speed for switching tiles.... ~1/4 a second == Every 12.5MHz completed increase if needed.
	 if(plyr_input[0] | plyr2_input[0]) begin //R button
		  if(curBallTile == MaxBallTiles)
								curBallTile <= 0;
		  else begin
								curBallTile <= curBallTile +1;
		  end
	end
	//player 1 and player 2 can switch their own tiles seperately...
	 if(plyr_input[1]) begin // L Button
		  if(curPaddleTileP1 == MaxPaddles) begin
								curPaddleTileP1 <= 0; end
		  else begin
								curPaddleTileP1 <= curPaddleTileP1 +1;
		  end
	 end
	  if(plyr2_input[1]) begin // L Button
		  if(curPaddleTileP2 == MaxPaddles) begin
								curPaddleTileP2 <= 0; end
		  else begin
								curPaddleTileP2 <= curPaddleTileP2 +1;
		  end
	 end
	 if(plyr_input[2] | plyr2_input[2]) begin //? button
		  if(invertColor == 1) begin
								invertColor<= 0; 
								end
		  else begin
								invertColor <= 1;
		  end
	 end
            
end

always @(posedge clk) begin
	
	case(current_tile)
		default: begin
			DO = 8'hAA;//bg tile?
			end
		P1paddle: begin
			case(curPaddleTileP1)
			1: DO = DO_2;
			2: DO = DO_8;
			default: DO = DO_7;
			endcase
 			end
		P2paddle: begin
			case(curPaddleTileP2)
			1: DO = ~DO_2;
			2: DO = DO_8;
			default: DO = DO_7;
			endcase
			end
		ball: begin
			case(curBallTile)
			default: DO = 16'hffff;
			5: DO = DO_1;
			1: DO = DO_3;
			2: DO = ~DO_4;
			3: DO = ~DO_5;
			4: DO = DO_6;
			endcase
			end
	
	endcase
end

always @(posedge clk_25mhz)
begin 

//	if(CounterX >= objXStart && CounterX < (objXStart+32)) begin
//		//Verticl sync
//		
//		if(CounterY >= objYStart && CounterY < (objYStart+32)) begin
		//$display("CX %d CY %d ADDR %d LOW_BYTE %x", CounterX,  CounterY, ADDR, DO_BG);
		//Verticl Sync
		if(draw_enable) begin
			offsetY <= CounterY-objYStart;
			offsetX <= CounterX-objXStart;
			ADDR[9:0] <= (offsetX + (offsetY<<<5))+3;
			
			// Set buffer

					if(invertColor)begin
					vga_Rbuf2 <= ~vga_Rbuf1;
					vga_Gbuf2 <= ~vga_Gbuf1;
					vga_Bbuf2 <= ~vga_Bbuf1;
					end
					else begin
						vga_Rbuf2 <= vga_Rbuf1;
						vga_Gbuf2 <= vga_Gbuf1;
						vga_Bbuf2 <= vga_Bbuf1;
					
					end
				

					vga_Rbuf1[2] <= DO[7] & inDisplayArea;//DO[7:5] & inDisplayArea;
					vga_Gbuf1[2] <= DO[4] & inDisplayArea;//DO[4:2] & inDisplayArea;
					vga_Bbuf1[1] <= DO[1] & inDisplayArea;//DO[1:0] & inDisplayArea;
					
					vga_Rbuf1[1] <= DO[6] & inDisplayArea;//DO[7:5] & inDisplayArea;
					vga_Gbuf1[1] <= DO[3] & inDisplayArea;//DO[4:2] & inDisplayArea;
					vga_Bbuf1[0] <= DO[0] & inDisplayArea;//DO[1:0] & inDisplayArea;
					
					vga_Rbuf1[0] <= DO[5] & inDisplayArea;//DO[7:5] & inDisplayArea;
					vga_Gbuf1[0] <= DO[2] & inDisplayArea;//DO[4:2] & inDisplayArea;

			end //CounterY if statement

	else begin
					offsetY <= CounterY-objYStart;
					offsetX <= CounterX-objXStart;
					ADDR[9:0] <= (offsetX + (offsetY<<<5))+3;
					
					if(invertColor)begin
					vga_Rbuf2 <= ~vga_Rbuf1;
					vga_Gbuf2 <= ~vga_Gbuf1;
					vga_Bbuf2 <= ~vga_Bbuf1;
					end
					else begin
						vga_Rbuf2 <= vga_Rbuf1;
						vga_Gbuf2 <= vga_Gbuf1;
						vga_Bbuf2 <= vga_Bbuf1;
					
					end
				

					vga_Rbuf1[2] <= DO_BG[7] & inDisplayArea;//DO[7:5] & inDisplayArea;
					vga_Gbuf1[2] <= DO_BG[4] & inDisplayArea;//DO[4:2] & inDisplayArea;
					vga_Bbuf1[1] <= DO_BG[1] & inDisplayArea;//DO[1:0] & inDisplayArea;
					
					vga_Rbuf1[1] <= DO_BG[6] & inDisplayArea;//DO[7:5] & inDisplayArea;
					vga_Gbuf1[1] <= DO_BG[3] & inDisplayArea;//DO[4:2] & inDisplayArea;
					vga_Bbuf1[0] <= DO_BG[0] & inDisplayArea;//DO[1:0] & inDisplayArea;
					
					vga_Rbuf1[0] <= DO_BG[5] & inDisplayArea;//DO[7:5] & inDisplayArea;
					vga_Gbuf1[0] <= DO_BG[2] & inDisplayArea;//DO[4:2] & inDisplayArea;

	
	end
end

endmodule